本日將深入介紹並實作第一個Regularization的技巧--Label Smooth,翻譯的話,好像可以稱作標籤平滑吧。
在前一日的文章內已經簡介提到,Label Smooth是一種在訓練時,將loss在與label進行計算時進行模糊化的技巧。這裡我們以最單純的Binary Classification為例,在只有兩類的情況下,label通常就是陽性(y=1)以及陰性(y=0)。
而Label Smooth的技術就是類似把這個0跟1,視為機率的概念,加入一個允許模糊的參數α,讓模型可以進行一個不會太極端狀態的學習,具體就是把label的定義改成類似下者:
例如,α=0.05的話,則陽性就是0.95、陰性就是0.05的機率下去算entropy(或是對應的loss)
因此α便是一個可以自訂來調整模糊空間的超參數,理論上也是需要Tune的。(Multiclass的情況可以參考這篇)
前一日也有提到,這是一個Regularization的方法,為的是降低Generalization Error。但思路是怎麼做的呢?在訓練反覆迭代的過程中,其實是會讓模型對於訓練集中的陽性樣本給出的結果越來越趨近1,而陰性樣本的結果則是越來越趨近於0。聽起來很不錯,但對嗎?這是不是也意味著,模型很有可能只能輸出很接近0跟1的結果?是不是模型在0到1之間的變化幅度會非常劇烈、鮮少出現中間值的情形?即使是未來的資料出現模型不曾學習過(或不擅長)的特徵,但模型卻沒辦法給出不確定的中間機率值,反而給出一個極端值呢?
Label Smooth便是一個處理這樣問題的技術,藉由調整label在計算loss時的數值,允許某一程度的模糊空間,進一步的避免訓練出來的模型變得只會去預測接近0與1的極端值。
另外補充,To Smooth or Not? When Label Smoothing Meets Noisy Labels有討論到noise rate的大小與label smooth與否及程度大小的研究,算是一個滿適合延伸的閱讀,有興趣的讀者可以再看看。
實作的部份則需要土法煉鋼一些東西,首先定義一個調整label的函數:
def label_smoother(tensor, label_smooth_fact):
return tensor * (1 - 2 * label_smooth_fact) + label_smooth_fact
然後讓我們在step內設定成,如果要做label smooth的話會進行更動:
def step(self, batch: Any):
inputs, labels = batch['img'].to(self.device, non_blocking=True), batch['labels'].to(self.device, non_blocking=True)
preds = self.forward(inputs)
if self.CONFIG['train']['label_smooth'] > 0:
smooth_label = label_smoother(labels, self.CONFIG['train']['label_smooth'])
loss = self.loss_function(preds, smooth_label.float())
else:
loss = self.loss_function(preds, labels.float())
return inputs, preds, labels, loss
接著就可以進入訓練環節了!
而另外補充,常見的Cross Entropy實作上比較容易,可藉由直接調整參數來達到,可參考文件
一樣可以參考這個Commit進行實作,這裡設定的α=0.05。
首先讓我們看看loss,可以看到Label Smooth的緣故,會使得BCE計算loss的方式跟預測的方式都會改變,也因此會造成一個很大的格差。
另外來看看AUC的部份,個人是猜測可能由於
最後可以來看一下對outcome的影響,可以發現確實大部分的結果從接近0,被移動到了
α=0.05的部份了。